import React, { FC, useRef, ChangeEvent, useState } from 'react'
import axios from 'axios'
import Dragger from './dragger'
import UploadList from './uploadList'

export interface UploadFile {
  uid: string
  size: number
  name: string
  status?: 'ready' | 'uploading' | 'success' | 'error'
  percent: number
  raw?: File
  response?: any
  error?: any
}
export interface UploadProps {
  action: string // 上传的地址
  defaultFileList?: UploadFile[] // 默认的文件列表
  // 若返回 false 或者 Promise 则停止上传
  beforeUpload?: (file: File) => boolean | Promise<File> // 传文件之前的钩子
  onProgress?: (percentage: number, file: UploadFile) => void // 文件上传时的钩子
  onSuccess?: (data: any, file: UploadFile) => void // 文件上传成功时的钩子
  onError?: (err: any, file: UploadFile) => void // 文件上传失败时的钩子
  onChange?: (file: UploadFile) => void // 文件状态改变时的钩子 失败也会改变
  onRemove?: (file: UploadFile) => void // 文件列表移除文件时的钩子
  headers?: { [key: string]: any } // 上传的请求头部
  name?: string // 上传文件字段名
  data?: { [key: string]: any } // 上传时附带的额外参数
  withCredentials?: boolean // 支持发送 cookie 凭证信息
  accept?: string // 接受上传的文件类型
  multiple?: boolean // 是否支持拖拽上传
  drag?: boolean // 是否支持拖拽上传
  children?: React.ReactNode
}

export const Upload: FC<UploadProps> = ({
  action,
  defaultFileList,
  beforeUpload,
  onProgress,
  onSuccess,
  onError,
  onChange,
  onRemove,
  name = 'file',
  headers,
  data,
  withCredentials,
  accept,
  multiple,
  children,
  drag
}) => {
  const fileInput = useRef<HTMLInputElement>(null)
  const [fileList, setFileList] = useState<UploadFile[]>(defaultFileList || []) // 文件列表

  // 点击上传按钮
  const handleClick = (e: React.MouseEvent) => {
    fileInput.current?.click()
  }
  // 更新文件列表
  const updateFileList = (updateFile: UploadFile, updateObj: Partial<UploadFile>) => {
    setFileList((prevList) => {
      return prevList.map((file) => {
        if (file.uid === updateFile.uid) {
          return { ...file, ...updateObj }
        } else {
          return file
        }
      })
    })
  }
  // 发送请求
  const post = (file: File) => {
    const _file: UploadFile = {
      uid: Date.now() + 'upload-file', // 生成一个唯一的文件id
      status: 'ready', // 文件状态
      name: file.name, // 文件名
      size: file.size, // 文件大小
      percent: 0, // 上传进度
      raw: file // 原始文件
    }
    setFileList((prevList) => {
      return [_file, ...prevList] // 将新文件添加到文件列表
    })
    const formData = new FormData() // 创建一个FormData对象
    formData.append(name || 'file', file) // 添加文件到FormData对象
    // 传入的额外参数
    if (data) {
      Object.keys(data).forEach((key) => {
        formData.append(key, data[key])
      })
    }
    // onChange?.(_file) // 文件状态改变时钩子
    axios
      .post(action, formData, {
        headers: {
          ...headers,
          'Content-Type': 'multipart/form-data'
        },
        withCredentials, // 支持发送 cookie 凭证信息
        // 上传进度
        onUploadProgress: (e) => {
          const percentage = e.total ? Math.round((e.loaded * 100) / e.total) : 0
          if (percentage < 100) {
            updateFileList(_file, {
              percent: percentage, // 进度百分比
              status: 'uploading'
            })
            _file.status = 'uploading'
            _file.percent = percentage
            onProgress?.(percentage, _file) // 文件上传时钩子
          }
        }
      })
      .then((resp) => {
        updateFileList(_file, { status: 'success', response: resp.data })
        _file.status = 'success'
        _file.response = resp.data
        onSuccess?.(resp.data, _file) // 文件上传成功时钩子
        onChange?.(_file) // 文件状态改变时钩子
      })
      .catch((err) => {
        updateFileList(_file, { status: 'error', error: err })
        _file.status = 'error'
        _file.error = err
        onError?.(err, _file)
        onChange?.(_file)
      })
  }
  // 上传文件
  const uploadFiles = (files: FileList) => {
    const postFiles = Array.from(files) // 将文件列表转化为数组
    postFiles.forEach((file) => {
      if (!beforeUpload) {
        post(file) // 直接上传
      } else {
        const result = beforeUpload(file) // 上传前
        if (result && result instanceof Promise) {
          result.then((processedFile) => {
            post(processedFile)
          })
        } else if (result !== false) {
          post(file) // 如果返回值不是false，则上传文件
        }
      }
    })
  }
  // 选择文件
  const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files
    if (!files) return
    uploadFiles(files)
    if (fileInput.current) fileInput.current.value = '' // 清空文件输入框
  }
  // 删除文件
  const handleRemove = (file: UploadFile) => {
    setFileList((prevList) => {
      return prevList.filter((item) => item.uid !== file.uid)
    })
    onRemove?.(file) // 文件移除时钩子
  }
  return (
    <div className="dog-upload-component">
      <div className="dog-upload-input" onClick={handleClick}>
        {drag ? (
          <Dragger
            onFile={(files) => uploadFiles(files)} // 拖拽结束上传文件
          >
            {children}
          </Dragger>
        ) : (
          children
        )}
        <input
          className="dog-file-input"
          style={{ display: 'none' }} // 隐藏
          ref={fileInput}
          onChange={handleFileChange}
          type="file"
          accept={accept}
          multiple={multiple}
        />
      </div>
      <UploadList fileList={fileList} onRemove={handleRemove} />
    </div>
  )
}
export default Upload
